#ifndef LIGHT_PATH_H
#define LIGHT_PATH_H
#include <hash_map>
#include <vector>
#include "globals.h"
#include "random.h"
#include "configurable.h"
#include "intersection.h"
namespace gemren
{	
	class light_paths : public configurable
	{
		template<typename T>
		struct pair_hasher
		{
			static const size_t bucket_size = 4;
			static const size_t min_buckets = 8;
			std::hash<size_t> her;
			size_t operator()(const std::pair<T,T>& p) const
			{
				return her(p.first*p.second);
			}
			bool operator()(const std::pair<T,T>& p1,  const std::pair<T,T>& p2) const
			{
				return p1.first < p2.first || (p1.first == p2.first && p1.second < p2.second);
			}
		};
		typedef std::pair<size_t, size_t> rtpair;
		typedef std::vector<size_t> trilist;
		typedef std::hash_map<rtpair, size_t, pair_hasher<size_t> > map_t;//<light_pair, vec_hasher<size_t>> set_t;
		struct chunkdata
		{
			map_t* sets;
			scene* sc;
			random* rand;
			size_t ray_count;
			chunkdata(): sets(0), sc(0), rand(0), candidates(0){}
			~chunkdata() 
			{
				delete [] sets; 
				free(rand); 
			}
			void reset(const size_t tcnt)
			{
				if(!sets)
					sets = new map_t[tcnt];

				if(!rand)
				{
					rand = (random*) malloc(tcnt*sizeof(random));
					for(size_t i = 0; i< tcnt; ++i)
						rand[i] = random(314*int(i));
				}
			}
			const trilist* candidates;
		};

	public:
		light_paths(): iteration_size(100000), max_iterations(1), threshold(0),
			tripairs(0), transmitters(0), hitcounts(0), totalcnts(0)
		{}
		~light_paths()
		{
			delete[] tripairs;
			delete[] totalcnts;
			delete[] transmitters;
			delete[] hitcounts;
		}
		
		void find_paths(scene* sc); //first step - finding transmitting candidates for each triangle  
		void compute_lighting(const scene* s, random* ran, intersection* i) const; // computation of caustic lighting for given spot
		
		virtual void announce_properties(const std::string& prefix, property_announcer announcer);
		void finish_caustic_mis_coef(intersection* i, const scene* sc) const;
	protected:
		//
		void get_transmitting_candidates(const scene* sc);
		//parse data from step 1
		void inform_triangles(scene* sc);
		//merges results from all threads to <pairs>
		size_t merge_paths(const map_t* npaths, const size_t count);
		
		//thread worker in step 1
		static void search_for_paths(::size_t tid, void* ptr, ::size_t elt);
		


		virtual err_t set_my_int(std::string& name, const int value);
		
		
		//indexed by triangles - receivers 
		size_t* tripairs,  //stores beginning of tri's transmitters in <transmitters> 
			  *totalcnts;  //total hits received by single tri
		
		//indexed by number from <tripairs>
		size_t *transmitters, //lists of transmitters for each triangles
			   *hitcounts;    //for each triangle's transmitter, store hit count ( = weight)	
		
		//results of step 1
		// "N rays hit <receiver>, being last transmitted by <transmitter>
		//map indexed by pair <transmitter, receiver>, values = N
		map_t pairs;
		
		//all transmitting triangles in the scene - used for choosing directions of rays in 1st step
		trilist transmitting_candidates; 
		
		//from iterator
		static inline size_t transmitter(map_t::const_iterator& it) { return it->first.first; }
		static inline size_t receiver(map_t::const_iterator& it) { return it->first.second; }
		
		//from pair
		static inline       size_t& transmitter(rtpair& p)       { return p.first;  }
		static inline       size_t& receiver   (rtpair& p)       { return p.second; }
		
		static inline const size_t& transmitter(const rtpair& p) { return p.first;  }
		static inline const size_t& receiver   (const rtpair& p) { return p.second; }
		
		
		static inline       size_t& hitcount(map_t::iterator& it)       { return it->second; }
		static inline const size_t& hitcount(map_t::const_iterator& it) { return it->second; }
		

		int iteration_size;
		int max_iterations;
		size_t threshold;
		chunkdata d;
	};
}
#endif